Skip to content

fix(rollout): train safely on incomplete groups - #6

Open
EazyReal wants to merge 42 commits into
vmax/ragged-transport-safetyfrom
vmax/mask-incomplete-rollout-groups
Open

fix(rollout): train safely on incomplete groups#6
EazyReal wants to merge 42 commits into
vmax/ragged-transport-safetyfrom
vmax/mask-incomplete-rollout-groups

Conversation

@EazyReal

@EazyReal EazyReal commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Purpose

Fork-local focused review for upstream areal-project/AReaL#1563. Do not merge this PR; the upstream PR is the landing authority.

Stack

Summary

  • represent unusable rollout slots as semantic None and retain every usable slot exactly once
  • derive min_usable_group_size from group-relative reward or advantage normalization; batch-relative estimators retain a usable singleton
  • preserve actual ragged group boundaries through normalization, redistribution, and sequence packing
  • backfill DP-dispatchable batches and synchronize optimizer minibatches without dropping a rank-local split
  • propagate deterministic workflow-contract violations terminally while keeping objective rejection retryable
  • retain the existing globally token-weighted PPO actor objective

There is deliberately no requires_peer protocol noun. The estimator owns its minimum usable sample count; the rollout boundary carries only that normalized integer contract.

Exact range

b0dbd4c423de3be706a3978fd9d1ced4678298e6..6b11d22af742baf18e3226dceb0ef6a2fd154a6c

Verification

  • affected CPU surface: 310 passed, 3 environment-dependent skips
  • distributed ragged/collective-safety surface: 2 passed, 3 environment-dependent skips
  • full pre-commit passed locally and in hosted CI
  • independent exact-head Megatron and Archon reviews found no actionable issue
  • CodeRabbit's collision and timeout findings are fixed; both retry-policy findings were withdrawn after the semantic contract was clarified; all four threads are resolved

A real multi-GPU Megatron/Archon pipeline canary is not available on this host.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ccaec6c-c412-4a4f-8ba2-36fc66efa7d2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds min_usable_group_size across rollout APIs and implementations, strengthens distributed rollout preparation and error synchronization, supports ragged grouping, introduces synchronized PPO microbatch scheduling, and adds group-level training metrics with corresponding tests and documentation.

Changes

Rollout API and propagation

Layer / File(s) Summary
Minimum usable group size propagation
areal/api/engine_api.py, areal/engine/*, areal/experimental/engine/*, areal/infra/controller/*, tests/test_train_controller.py
Public rollout methods, engine adapters, and controllers accept and forward min_usable_group_size.
Distributed rollout preparation and redistribution
areal/infra/dist_rollout.py, tests/test_data_redistribution.py, tests/torchrun/redistribute.py
Ragged trajectory gathering, head-only preparation, retry handling, and collective error synchronization are added.
Grouped rollout usability and metrics
areal/infra/remote_inf_engine.py, tests/test_grouped_rollout_workflow.py, tests/test_incomplete_rollout_groups.py, docs/*/reference/rollout_workflow.md
Grouped workflows enforce usable-slot thresholds, validate merged slot cardinality, and record group statistics; documentation reflects the new behavior.
PPO group metrics and synchronized microbatches
areal/utils/data.py, areal/trainer/ppo/*, areal/trainer/rl_trainer.py, tests/test_utils.py, tests/test_incomplete_rollout_groups.py
PPO updates use synchronized microbatch schedules, transport-only padding, group metrics, and trainable-batch collection constraints.
Ragged partitioning and validation
areal/utils/seqpack.py, areal/utils/data.py, areal/infra/controller/train_controller.py, tests/test_seqpack.py, tests/test_eval_dispatch.py, tests/test_reward_norm_variable_group.py, tests/v2/training_service/test_data_proxy_unit.py
Uneven partitions are supported while invalid rank counts and missing divisibility metadata remain rejected.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: garrett4wade, dingzhiqiang

Poem

A rabbit hopped through rollout lanes,
Sorting groups through distributed trains.
Tiny slots found their proper place,
Microbatches marched in pace.
“Usable!” cried the bunny bright—
Metrics bloomed by lantern light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: making rollout training safe for incomplete groups.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vmax/mask-incomplete-rollout-groups

Comment @coderabbitai help to get the list of available commands.

@EazyReal
EazyReal force-pushed the vmax/mask-incomplete-rollout-groups branch 6 times, most recently from 50b6ffa to 682003a Compare July 24, 2026 11:06
@EazyReal
EazyReal force-pushed the vmax/mask-incomplete-rollout-groups branch from 8850bb4 to 18b64a8 Compare July 29, 2026 05:57
@EazyReal EazyReal changed the title feat(rollout): mask untrainable incomplete groups fix(rollout): train safely on incomplete groups Jul 29, 2026
@EazyReal
EazyReal changed the base branch from review/issue-1559-base to vmax/ragged-transport-safety July 29, 2026 10:18
@EazyReal

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@EazyReal

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
areal/api/engine_api.py (1)

205-226: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound min_usable_group_size before rollout retries.

A grouped rollout can provide at most group_size usable slots. If min_usable_group_size > group_size, arun_episode rejects every group, and dynamic preparation can retry forever. Validate 1 <= min_usable_group_size <= group_size at the shared rollout boundary and document the constraint.

Also applies to: 247-269, 750-784, 858-887, 908-954

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@areal/api/engine_api.py` around lines 205 - 226, Validate
min_usable_group_size at the shared rollout boundary before any rollout retries,
requiring 1 <= min_usable_group_size <= group_size; reject invalid values early
so arun_episode and dynamic preparation cannot retry indefinitely. Update the
parameter documentation and all corresponding rollout entry points to state this
constraint, including the methods covering the referenced grouped rollout paths.
🧹 Nitpick comments (3)
tests/test_train_controller.py (1)

579-608: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover non-default forwarding.

Both tests exercise only the default value. Call the controller methods with group_size=2 and min_usable_group_size=2, then assert that 2 reaches the rollout mock.

Also applies to: 610-638

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_train_controller.py` around lines 579 - 608, Update the
prepare_batch delegation tests, including the additional test covering the same
path, to pass group_size=2 and min_usable_group_size=2 to the controller and
assert mock_rollout.prepare_batch receives both values as 2 instead of the
defaults.
areal/infra/remote_inf_engine.py (1)

724-730: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider documenting min_usable_group_size on rollout_batch/prepare_batch too.

Only submit gained the parameter docs (Lines 1250-1251); the other two public entrypoints now accept the same argument with no docstring entry.

Also applies to: 751-751, 849-849, 1233-1233, 1250-1251, 1275-1275, 1328-1328, 1361-1361, 1415-1415

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@areal/infra/remote_inf_engine.py` around lines 724 - 730, Document the
min_usable_group_size parameter in the docstrings for the public rollout_batch
and prepare_batch entrypoints, matching the existing description and behavior
documented for submit. Ensure each entrypoint’s parameter list explains the
valid range and purpose without changing the validation or implementation.
tests/torchrun/redistribute.py (1)

14-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

_HybridTrainEngine omits cpu_group/config, which the coordinator touches on the success path.

Both assertions in _test_hybrid_error raise before _broadcast_and_redistribute_trajectories reaches dist.barrier(group=self.train_engine.cpu_group), so this passes today. Adding a cpu_group (and a config stub) makes the fake engine resilient if the error path ever shifts, instead of failing with a confusing AttributeError.

♻️ Suggested hardening
 class _HybridTrainEngine:
     def __init__(self, rank, dp_group, model_group):
         self.rank = rank
         self.data_parallel_group = dp_group
         self.context_and_model_parallel_group = model_group
+        self.cpu_group = model_group
+        self.config = None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/torchrun/redistribute.py` around lines 14 - 24, Update
_HybridTrainEngine to define the cpu_group attribute and a config stub expected
by _broadcast_and_redistribute_trajectories, initializing them in __init__
alongside the existing rank and process-group fields. Preserve the current test
behavior while ensuring the fake engine can safely reach the coordinator’s
success path without raising AttributeError.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@areal/infra/dist_rollout.py`:
- Around line 346-390: Bound the retry behavior in _prepare_until_dispatchable
when dynamic_bs is enabled: track attempts, emit progress diagnostics comparable
to _collect_trainable_rollout_batch, and apply the established retry/backoff
approach if available. After the configured limit is reached without a
dispatchable batch, raise a clear RuntimeError instead of looping indefinitely;
preserve the existing synchronization and fixed-batch behavior.

In `@areal/trainer/rl_trainer.py`:
- Around line 102-128: Bound retries in _collect_trainable_rollout_batch when
dynamic_bs is enabled by enforcing a finite attempt limit (or equivalent
timeout) while collecting undersized batches. After the limit is exceeded, raise
a clear actionable error that includes the collected and required group counts;
preserve the current successful return and fixed-batch RuntimeError behavior.

In `@areal/utils/data.py`:
- Around line 934-989: Update split_training_batch_into_microbatches so
effective_n_mbs provides at least enough slots for the largest local microbatch
contribution, while preserving the requested n_mbs limit when it is sufficient.
Replace the scheduled-slot assert with explicit collision handling that fails
loudly rather than overwriting data. Add coverage for a rank whose split count
exceeds n_mbs, such as by forcing a small max_tokens_per_mb, including the
synchronized schedule path used by PPO actor and critic updates.

In `@tests/test_data_redistribution.py`:
- Around line 52-78: Add timeout=60 to the subprocess.run invocation in
test_redistribute_ragged_cpu, matching the existing hybrid redistribution test
while preserving the current command and output assertions.

---

Outside diff comments:
In `@areal/api/engine_api.py`:
- Around line 205-226: Validate min_usable_group_size at the shared rollout
boundary before any rollout retries, requiring 1 <= min_usable_group_size <=
group_size; reject invalid values early so arun_episode and dynamic preparation
cannot retry indefinitely. Update the parameter documentation and all
corresponding rollout entry points to state this constraint, including the
methods covering the referenced grouped rollout paths.

---

Nitpick comments:
In `@areal/infra/remote_inf_engine.py`:
- Around line 724-730: Document the min_usable_group_size parameter in the
docstrings for the public rollout_batch and prepare_batch entrypoints, matching
the existing description and behavior documented for submit. Ensure each
entrypoint’s parameter list explains the valid range and purpose without
changing the validation or implementation.

In `@tests/test_train_controller.py`:
- Around line 579-608: Update the prepare_batch delegation tests, including the
additional test covering the same path, to pass group_size=2 and
min_usable_group_size=2 to the controller and assert mock_rollout.prepare_batch
receives both values as 2 instead of the defaults.

In `@tests/torchrun/redistribute.py`:
- Around line 14-24: Update _HybridTrainEngine to define the cpu_group attribute
and a config stub expected by _broadcast_and_redistribute_trajectories,
initializing them in __init__ alongside the existing rank and process-group
fields. Preserve the current test behavior while ensuring the fake engine can
safely reach the coordinator’s success path without raising AttributeError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 24659cf9-e44a-4e85-bf21-a9c4bf2c4632

📥 Commits

Reviewing files that changed from the base of the PR and between b0dbd4c and 18b64a8.

📒 Files selected for processing (27)
  • areal/api/engine_api.py
  • areal/engine/fsdp_engine.py
  • areal/engine/megatron_engine.py
  • areal/engine/sglang_remote.py
  • areal/engine/vllm_remote.py
  • areal/experimental/engine/archon_engine.py
  • areal/infra/controller/rollout_controller.py
  • areal/infra/controller/train_controller.py
  • areal/infra/dist_rollout.py
  • areal/infra/remote_inf_engine.py
  • areal/trainer/ppo/actor.py
  • areal/trainer/ppo/critic.py
  • areal/trainer/rl_trainer.py
  • areal/utils/data.py
  • areal/utils/seqpack.py
  • docs/en/reference/rollout_workflow.md
  • docs/zh/reference/rollout_workflow.md
  • tests/test_data_redistribution.py
  • tests/test_eval_dispatch.py
  • tests/test_grouped_rollout_workflow.py
  • tests/test_incomplete_rollout_groups.py
  • tests/test_reward_norm_variable_group.py
  • tests/test_seqpack.py
  • tests/test_train_controller.py
  • tests/test_utils.py
  • tests/torchrun/redistribute.py
  • tests/v2/training_service/test_data_proxy_unit.py

Comment thread areal/infra/dist_rollout.py
Comment thread areal/trainer/rl_trainer.py
Comment thread areal/utils/data.py
Comment thread tests/test_data_redistribution.py Outdated
@EazyReal
EazyReal force-pushed the vmax/mask-incomplete-rollout-groups branch from 18b64a8 to 6b11d22 Compare July 29, 2026 19:21
@EazyReal

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@EazyReal

Copy link
Copy Markdown
Owner Author

CodeRabbit disposition on current head 6b11d22:

  • fixed the microbatch-slot collision and added the forced local_count > n_mbs regression
  • added the distributed subprocess timeout
  • centralized 1 <= min_usable_group_size <= group_size validation before retries
  • covered non-default forwarding and documented the constraint on all rollout entry points
  • kept dynamic objective rejection open-ended by design; terminal contract/configuration failures now propagate immediately across local and remote execution
  • did not add unused cpu_group/config fields to an error-path-only test fake, or filler docstrings solely to raise a generic coverage percentage

Current evidence: 310 affected tests passed with 3 environment skips, distributed ragged tests passed, and full pre-commit passed.

wuallen57730 and others added 8 commits July 31, 2026 10:29
…roject#1569)

agenerate read the weight version twice: once when building the
request and again when recording output_versions. A weight update
landing in between labelled tokens with a version that did not
generate them, which skews staleness checks such as the decoupled
loss and rejection sampling.

Pin the version before the request goes out and reuse it when
recording, so each segment of a trajectory carries the version that
actually served it.
…real-project#1544)

use_deterministic_algorithms previously set deterministic_mode only on
the built model's config. Several Megatron-Core and TransformerEngine
code paths consume determinism settings at module construction and
cache them in instance state:

- VocabParallelEmbedding copies config.deterministic_mode at __init__;
  without it the nondeterministic F.embedding backward is used.
- TEDotProductAttention validates NVTE_ALLOW_NONDETERMINISTIC_ALGO
  against deterministic_mode only at __init__, and TE snapshots its
  deterministic flag from that env var and the global torch switch at
  construction.

Setting the flag after the model was built therefore engaged only
runtime consumers (loss fusions, schedules) and silently left the
layer-level kernels nondeterministic.

Changes:
- Set deterministic_mode on the TransformerConfig before the model is
  built, keeping the post-build call for runtime consumers.
- Select AttnBackend.flash under deterministic mode: Megatron-Core owns
  the NVTE_*_ATTN selection env vars and asserts they match the config,
  and the cuDNN fused-attention deterministic backward needs workspaces
  that grow prohibitively with context length.
- Export NVTE_ALLOW_NONDETERMINISTIC_ALGO=0 to trainer processes from
  the launchers and to megatron worker specs in single-controller mode,
  so it is in place before TransformerEngine reads it regardless of TE
  version; warn when the setting may have come too late.

With this, repeated runs of the same batch produce bitwise-identical
training stats, including grad_norm.
…(incl. areal-project#1577) (areal-project#1579)

* fix: emit PEFT-standard disk LoRA adapter keys so vLLM can load them, and align sglang best-effort unload test assertion (areal-project#1577)

* test(inference-service): raise VLM controller init timeout to 600s to avoid vLLM startup timeout
…real-project#1573)

Upstream SGLang does not enable the mamba cache path for the Bailing
hybrid MoE family, so radix cache stays off and prefix reuse is lost.
Add an opt-in script that applies the same eight edits the Ling hybrid
fork carries, covering SGLang 0.5.9 and 0.5.10.post1, which moved the
DP-attention padding helper behind a dp_size prologue.

The script is reversible and supports a dry run, so the target tree can
be checked before anything is written.
…ndering (areal-project#1499)

* fix(openai): align proxy tool schemas with sglang chat-completions rendering

The proxy path renders the request `tools` block through LiteLLM-style
dicts, while sglang's native /v1/chat/completions route round-trips
tools through its pydantic `Tool` model before applying the chat
template. The two renderings differ in field order and default-field
presence (e.g. sglang's `Function` dumps `strict: false` while the
proxy omits it), so the same trajectory produces different prompt
token ids on the two paths and training rewards drift from evaluation
results.

Round-trip tools through sglang's `Tool` model on the proxy path so
the dicts fed to `apply_chat_template` byte-match sglang's own
rendering. Flat Responses `FunctionToolParam` entries (top-level
`name`/`parameters`, no `function` key) are first normalized to the
nested Chat shape that both sglang's model and the chat template
expect. The alignment only runs when the engine class name identifies
an sglang backend; other backends are left unaligned, and per-tool
validation failures fall back to the original dict.

Ported from a verified internal fix.

* test(openai): cover tool schema alignment against sglang rendering

Assert the aligned dicts match what sglang's own Tool model produces, so
a regression that reintroduces the prompt-token drift fails here rather
than showing up as a train/eval reward mismatch.

Also pin the documented fallbacks: flat Responses tools normalize to the
nested Chat shape, a single invalid tool does not fail the batch, and
unsupported entries pass through unchanged.
)

* feat(mcore): apply fp32 lm head forward when enabled

enable_fp32_lm_head only reached the model through mbridge extra args, so
it was dropped for configs whose TransformerConfig rejects the field and
the flag silently had no effect. Patch the lm_head/output_layer forward
after the model is built instead, so the existing flag applies on every
Megatron construction path.

The patch is skipped when the flag is off, when Megatron already provides
a native fp32 column-parallel head, and when a module was already
patched, and it never runs for critic models, which replace the output
layer with a value head.

* fix(mcore): forward the tensor-parallel group from the fp32 lm head

The patched forward mirrors ColumnParallelLinear, which passes self.tp_group to
copy_to_tensor_model_parallel_region and
gather_from_tensor_model_parallel_region. Both calls omitted it, so they fell
back to the default tensor-model-parallel group and ran the collectives on the
wrong ranks whenever the head was built with a non-default group. The sequence
parallel path already forwarded the group, so the two behaved inconsistently.

Take the group once and route all three calls through a helper that drops the
keyword on megatron-core releases that do not accept it. Runs on the default
group are unaffected: the wrappers resolve None through
get_tensor_model_parallel_group_if_none, so passing it explicitly is identical
to omitting it.

Document the patch on _fp32_lm_head_forward_impl: what it fixes, the
megatron-core version it was verified against, and how it drifts if upstream
changes the forward or the collective signatures.
…l-project#1572)

Rejection sampling narrows the loss mask, so the existing token counters no
longer describe what the update actually trained on. Report the total, valid and
masked token counts alongside the masked ratio, and split the log-prob drift
into signed and absolute forms so a run's staleness is visible per step.

prompt_len is derived from the first trained position rather than the difference
of the two mask sums, which stops reporting the prompt as longer than it is once
rejection removes generated tokens from the loss mask. The mask reaching
_ppo_update is rolled left by one, so the roll is undone before the lookup.
Address review on the incomplete-group transport and collection paths:

- Replace the serial per-item broadcast fallback for ragged trajectory
  lists with one metadata all-gather plus one padded all-gather per
  dtype/device bucket, so unequal group counts cost a handful of
  collectives instead of sum(lengths) container broadcasts. Ranks with
  zero trajectories join the same collectives via the gathered metadata,
  and gathered tensors are cloned out of the padded buffers so a skewed
  distribution does not pin world_size x max-payload memory.
- Split redistribute_trajectories into its gather phase and a pure-local
  packing phase, and recover only the latter: every head holds the same
  gathered data there, so failures are symmetric and always reach the
  error sync; a failure inside a collective now propagates instead of
  posting mismatched operations at peers.
- Abort dynamic preparation only on a sustained stall: at least eight
  consecutive rounds that add no trainable group AND thirty minutes
  without progress. Both signals ride the existing per-round all-gather,
  so every head raises on the same iteration and the coordinated error
  path turns a silent SPMD spin into a terminal error on all ranks,
  while fast legitimate all-reject bursts (e.g. a staleness flush after
  a weight update) stay alive.

Refs: areal-project#1563
Signed-off-by: EazyReal <8047065+EazyReal@users.noreply.github.com>
Address review on estimator-owned group minimums and the tightened
workflow contract:

- NormConfig gains uses_group_statistics and PPOActorConfig owns
  minimum_usable_group_size, replacing the trainer-side free function
  and its ad-hoc shape duck-typing.
- A singleton target group is complete by definition, so group
  statistics no longer hard-fail n_samples=1 configs; the previous
  ValueError broke examples/openclaw on startup while the v2 rollout
  path skipped the check entirely. Config-level degeneracy stays a
  UserWarning, as on main.
- The trainer collection loop shares the empty-round streak plus
  wall-clock stall bound and rate-limits its progress warnings.
- WorkflowContractError now names the two remedies (one sample per
  episode or batch-level normalization), and the one-sample-per-slot
  contract is documented in the RolloutWorkflow docstring and the
  grouped-rollout reference (EN/ZH), scoped honestly to grouped
  rollouts: n_samples=1 installs no group wrapper and is not checked.
- Correct docstrings that still described divisibility-based dispatch:
  balanced_greedy_partition, _pad_eval_batch, and the v2 data-proxy
  dispatcher module.

Refs: areal-project#1563
Signed-off-by: EazyReal <8047065+EazyReal@users.noreply.github.com>
@EazyReal
EazyReal force-pushed the vmax/mask-incomplete-rollout-groups branch from 3e7fa87 to 9392e20 Compare August 9, 2026 23:04
EazyReal and others added 27 commits August 10, 2026 06:46
Reviewer request on areal-project#1563: expose the partial-group trainability
threshold instead of only deriving it. PPOActorConfig gains
min_usable_group_size (default None); None keeps the derived value
(2 when reward_norm/adv_norm uses group statistics, 1 for singleton
target groups, else 1), an explicit value replaces it and is still
validated against group_size at workflow setup.

Signed-off-by: Max Wang <maxwill@vmax.ai>
With n_samples=1 every prompt group is a singleton, so group mean
centering erases the task reward. Batch centering keeps a live signal;
singleton group std is already pinned to 1 and stays a no-op.

Signed-off-by: Max Wang <maxwill@vmax.ai>
…e-rollout-groups

# Conflicts:
#	areal/trainer/ppo/actor.py
Reviewer request on areal-project#1563: an explicit min_usable_group_size below 2
combined with group-relative normalization would train lone survivors
that have no group peers to normalize against, so PPOActorConfig now
rejects it at construction. The derived default is unaffected.

Signed-off-by: Max Wang <maxwill@vmax.ai>
std_level: group is not the no-op I claimed on the review thread: the
OpenAI-proxy agent exports one row per interaction, so compute_advantages
passes per-episode row counts as group sizes and a k-turn episode's
identical rewards collapse to sign(r - mean) * sqrt((k-1)/k), discarding
reward magnitude. Batch std matches adv_norm and keeps the signal; the
group_size line had no remaining consumer.

Signed-off-by: Max Wang <maxwill@vmax.ai>
Two exactness gaps around the new config field. The v2 rollout path never
consumes it, so an explicit setting now fails fast there (matching
RolloutControllerV2's rejection of reward_normalization and
drop_incomplete_group) and the help text names the v1 scope. The
one-sample-per-slot contract is armed by the resolved minimum, not by
group normalization itself, so the error message, arun_episode docstring,
and reference docs now name the real trigger and include unsetting
actor.min_usable_group_size among the remedies; the agent tutorial and
export-style reference gain the same caveat where they teach
export_style: individual.

Signed-off-by: Max Wang <maxwill@vmax.ai>
* feat: update awex to 0.8.0

Bump the pinned awex weight-synchronization dependency in both the
SGLang and vLLM manifests and regenerate the corresponding lockfiles.

awex 0.8.0 adds Qwen3-MoE weight conversion with dtype-only IPC
grouping and an SGLang colocate plugin with colocate P2P transfer
fixes. It also corrects the fused qkv split to be GQA-aware and forces
CUDA IPC handles to close before signalling the train side.

All 25 awex symbols imported by AReaL were audited against v0.8.0 and
are unchanged in both name and signature, so no call-site changes are
required. awex declares no runtime dependencies, so resolution is
otherwise unaffected.

Key changes:
- Pin awex==0.8.0 in pyproject.toml and pyproject.vllm.toml
- Regenerate uv.lock and uv.vllm.lock

* chore: update Dockerfile node version and lock npm version
…ct#1596)

Use a Gloo sidecar for separated AWEX liveness and completion barriers so the NCCL process group remains dedicated to model-weight payloads. Fence SGLang device writes before the CPU-side completion barrier so success is not reported while CUDA copies are still pending.

Key changes:
- create matching NCCL and Gloo groups for FSDP, Megatron, and SGLang adapters
- route setup and completion barriers through the sidecar
- synchronize SGLang device copies before the completion barrier
- destroy both groups and test lifecycle and ordering
…ect#1589)

Preserve model-declared worker order regardless of registration timing, and prevent concurrent SGLang servers from sharing Triton cache entries.

Key changes:
- Rebuild router candidates in model address order
- Deduplicate model addresses without changing routing weight
- Give every SGLang fork an index- and UUID-scoped cache directory
- Preserve existing controller-to-worker cache root precedence
…on Slurm (areal-project#1584)

SchedulingSpec had no way to reach sbatch's --reservation or --exclusive, so
runs that need a reserved partition had to be launched by hand outside the
scheduler.

Explicit env_vars were also lost: the scheduler applied AReaL's forwarding and
the thread-count defaults on top of them, so a spec asking for a specific
OMP_NUM_THREADS or allocator config silently got the framework value. Snapshot
the user's mapping and re-apply it last, which also lets colocated roles carry
per-role settings that must differ between actor and rollout.
Allow optimizer configs to select an absolute scheduler warmup while preserving the proportional fallback across FSDP, Megatron, and Archon.

Validate shared scheduler boundaries and keep Megatron resume initialization independent of the active warmup config.

Key changes:
- Add fixed warmup resolution with cross-engine validation
- Align Megatron decay and resume scheduler parameters
- Add boundary and Megatron integration regression tests

Co-authored-by: 峯回 <dh183333@antgroup.com>
…-project#1602)

The normalization section still claimed that `group_size` must be
specified for group-level normalization. Since areal-project#1454, group boundaries
come from the rollout batch metadata (`TrajBatchMeta.traj_group_sizes`)
that `PPOActor._compute_advantages` forwards to `Normalization`, and
`NormConfig.group_size` only serves as the fixed-stride fallback when
that metadata is absent. Mirrored in the Chinese translation.

Co-authored-by: EazyReal <8047065+EazyReal@users.noreply.github.com>
…t#1594)

* fix(models): support FP32 operands with chunked LM head

Keep enable_fp32_lm_head orthogonal to chunked logits while avoiding repeated local vocab-weight casts across sequence chunks.

Key changes:
- Reuse one FP32 weight conversion per LM-head forward and backward
- Preserve TP/SP gradient communication and FP32 main_grad accumulation
- Cover flag combinations, gradients, and distributed execution

* test(models): account for chunked bias reduction order

Low-precision chunked backward sums bias gradients per chunk, while the full reference reduces all tokens at once. Compare the old BF16/FP16 path with an explicit relative tolerance and keep strict parity for FP32 operands.

* refactor(models): scope FP32 projection helper

Keep the full-sequence FP32 projection implementation private to the native linear class so unrelated call sites cannot invoke it accidentally.
…al-project#1575)

* feat(scheduler): support grouped colocation in the Ray scheduler

Colocation previously required the colocated role to match the target
role's replica count, which rules out AWEX-style colocation where a few
multi-GPU inference workers share the GPUs of many single-GPU trainer
workers (e.g. 16 x 4-GPU SGLang servers over 64 x 1-GPU actors).

When replica counts differ but the colocated role's total GPU demand
exactly reuses the target role's GPUs, route worker creation to a
grouped path: chunk each target node's physical GPUs into contiguous
per-worker groups (never crossing nodes), pin one zero-GPU process
launcher to each target node via node affinity, and start worker
processes with explicit physical gpu_devices so CUDA_VISIBLE_DEVICES
carries physical indices exactly like the Slurm launcher. Grouped roles
own their workers and launchers, are not registered as colocated
aliases, and create no placement groups, so readiness discovery and
teardown follow the standard paths.

* fix(scheduler): accept a device-free Ray driver node

RayScheduler probed accelerators on the driver process only, so a CPU-only
head node aborted with "does not support CPU-only clusters" even when every
Ray worker exposed GPUs. Fall back to the advertised cluster resources when
the driver itself owns no device.
…l-project#1603)

Megatron-Bridge materializes a provider separate from the transformer config. Apply prebuild determinism to that provider before finalization so construction-time consumers inherit the requested settings.
Avoid wrapping local targets with stdbuf when the effective target
environment already defines LD_PRELOAD. GNU stdbuf appends libstdbuf to
the value, which breaks TMS CDLL loading during offload.

Preserve env=None versus explicit env={} semantics while retaining line
buffering for targets without LD_PRELOAD.

Refs: areal-project#1570

Signed-off-by: yaoyaoshiguonan <yaoyaoguonan@outlook.com>
The no_save_optim help text told users the flag was "required when
using use_distributed_optimizer with Megatron (flattened_range
incompatibility)". That workaround was obsoleted by areal-project#1468, which
switched the Megatron checkpointer to dp_reshardable optimizer
sharding, so saving optimizer state works again on the pinned
megatron-core 0.17.0.

Following the stale advice now costs users silently: recovery resumes
with a freshly initialized optimizer, continuing at full learning rate
with reset Adam moments. Reword both optimizer-skip flags to describe
the actual trade-off, and add tests pinning the behavior the help text
now promises.

Key changes:
- Reword no_save_optim/no_load_optim help in RecoverConfig
- Assert both flags default to off, guarding against reintroducing the
  workaround as a default
- Assert the help text no longer documents the flags as required
- Cover RecoverHandler threading both flags into SaveLoadMeta.with_optim
- Regenerate docs/{en,zh}/cli_reference.md

Refs: areal-project#1341, areal-project#1468
Allow Qwen3-VL models to merge multimodal embeddings before packing while preserving existing padded-only and wrapper-owned paths.

Key changes:
- Route dense and MoE Qwen3-VL through model-owned THD
- Reconstruct padded inputs and restore packed model outputs
- Add routing, alignment, parity, and distributed forward coverage
…ct#1605)

* feat(engine): support Qwen3-VL with native AWEX colocate

Preserve the complete multimodal Hugging Face config during colocate\nmetadata exchange so AWEX can resolve both the language model and vision\ntower sharding contracts.\n\nKey changes:\n- Publish composite Qwen3-VL inference configuration\n- Delegate nested config parsing to native AWEX\n- Add Dense and MoE colocate contract tests

* fix(engine): preserve Qwen3-VL-MoE composite config

SGLang stores only the text config on the Qwen3-VL-MoE runtime model.\nRead the original Hugging Face config from ModelRunner so AWEX also\nreceives the vision tower metadata required for weight sharding.
…1617)

Point Ascend users to the ascend-v1.0.5 branch and refresh the guide to match the published A2 and A3 images. Document HDK 25.5.1, CANN 9.0.1, and the verified runtime stack, and keep the Chinese translation aligned with the English guide.
…1618)

Proxy engines keep issuing stale requests during rollout weight updates.
Pause them before normal workers and resume them before dispatch restarts.
* feat: add turn-level GAE support

Treat each generated turn as a GAE timestep while preserving the token-level default and token-local KL regularization.

Key changes:
- propagate and validate token-aligned turn IDs
- compute turn-level advantages without full CPU sequence scans
- filter structural metadata at FSDP and Archon model boundaries
- document the new selector and add focused regression tests

* feat(trainer): add dynamic per-sample GAE lambda

Allow GAE lambda to vary by trajectory using effective token or turn lengths while preserving static float behavior.

Key changes:
- Resolve custom lambda functions and keyword arguments from config
- Add VAPO length-adaptive GAE with empty-trajectory handling
- Validate per-sample lambda tensors and cover token and turn modes

* perf(trainer): reduce GAE preprocessing overhead

Hoist loop-invariant tensor work and bypass dynamic trajectory length construction when GAE lambda is static.

* feat(trainer): add relative-position GAE lambda

* fix(trainer): allow token lambda without turn metadata

Keep custom token-level GAE lambda functions compatible with rollout workflows that do not emit turn IDs, while preserving the metadata requirement for turn-level GAE.

* docs: regenerate CLI reference for GAE options

Keep the generated configuration reference aligned with the current main branch after porting the AntCode GAE changes.

* docs: document flexible GAE configuration

Explain token- and turn-level recurrences, KL and critic semantics, dynamic lambda strategies, and custom workflow turn IDs in English and Chinese.\n\nFix CLI default rendering for dataclass factories and cover it with unit tests.

* refactor(trainer): extract GAE helpers

Keep PPOActor focused on training orchestration by moving GAE kernels, turn metadata validation, and lambda context construction into a dedicated module.

---------

Co-authored-by: Wenhao Zhou <miumiu.zwh@antgroup.com>
Enable Qwen3.5 Dense and MoE registry recovery for AWEX colocated weight updates and provide a two-node Megatron/SGLang Geometry3K configuration.
…1601)

Keep prior versioned LoRA names registered in the vLLM server so
in-flight rollout requests do not break after adapter refreshes.
Also fixed lora example yamls as per some latest syntax.

Key changes:

- Retain old runtime LoRA aliases in areal_vllm_server.py
- Add a regression test for alias retention
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.